#ifndef COORD_H_
#define COORD_H_

#define COORD_DIM 3

#define COORD_X 0
#define COORD_Y 1
#define COORD_Z 2

/*
 * Clase Coordenada 3D que agrupa varias funciones sobre el
 * manejo de coordenadas.
 *
 * Ofrece las siguientes operaciones vectoriales:
 *    Operador +:    Coord1 + Coord2 retorna Coord3
 *    Operador +=:   Coord1 += Coord2
 *
 *    Operador -:    Coord1 - Coord2 retorna Coord3
 *
 *    Normalizar:    Coord1.normalizar() retorna Coord2 normalizado
 *
 *    Operador *:    Coord1 * float retorna Coord2
 *    Operador *:    float * Coord1 retorna Coord2
 *    Operador *=:   Coord1 *= float
 *
 *    Operador *:    Coord1 * Coord2 returna un float
 *
 *    cross:         Coord1.cross(Coord2) Retorna el producto vectorial Coord3
 *
 * Ademas ofrece las siguientes construcciones:
 *    
 *    Constructor por default, con las coordenadas iguales a 0
 *    Constructor por copia
 *    Constructor(x, y, z) que especifica cada coordenada
 *
 *    Operador =:    Coord1 = Coord2   Copia
 *
 *    Las siguientes operaciones sobre arrays deben ser tales que
 *    las dimensiones de estos sean iguales a las dimensiones de
 *    la clase Coord (esto es igual a 3)
 *
 *    Operador =:    Coord1 = float[]  Copia desde un array
 *    Constructor por copia:  Coord coord1(float[]) desde un array
 *
 * Convenientemente hay un operador casteo que retorna de la clase
 * Coord un array de floats con sus coordenadas
 * Junto con esto ademas hay operadores para el acceso a cada coordenadas (xyz)
 *    
 *    Coord coord1(1, 2, 3);
 *    const float *coords = coord1;
 *
 *    assert(coords[0] == coord1.x());
 *    assert(coords[1] == coord1.y());
 *    assert(coords[2] == coord1.z());
 *
 * A diferencia del operador casteo (que solo retorna un array constante),
 * los valores devueltos por los metodos x(), y(), z() son referencias mutables:
 * Esto quiere decir que pueden funcionar como lvalue
 *
 *    Coord coord1(-999, 2, 3);
 *    coord1.x() = 1;
 *
 *    assert(coord1.x() == 1);
 * 
 * */
class Coord {
   float data[COORD_DIM];

   public:
      Coord();
      Coord(const Coord &aCopiar);
      Coord(float x, float y, float z) ;
      Coord(const float *f) ;

      Coord& operator=(const float *f) ;
      Coord& operator=(const Coord &aCopiar) ;

      void set(float x, float y, float z) ;

      operator const float*() const ;

      void operator+=(const Coord &b); 
      Coord operator+(const Coord &b) const ;
      Coord operator-(const Coord &b) const ;
      Coord operator*(const float e) const ;
      float operator*(const Coord& e) const ;
      friend Coord operator*(const float e, const Coord &c);
      void operator*=(const float e) ;

      Coord normalizar() const ;
      Coord cross(const Coord &c) const ;

      inline float& x() { return data[COORD_X]; }
      inline const float& x() const { return data[COORD_X]; }

      inline float& y() { return data[COORD_Y]; }
      inline const float& y() const { return data[COORD_Y]; }

      inline float& z() { return data[COORD_Z]; }
      inline const float& z() const { return data[COORD_Z]; }
};

#undef COORD_X
#undef COORD_Y
#undef COORD_Z

#undef COORD_DIM

#endif
